Terraform Your Tunnels

Table of Contents
Ok. So this might not be that new for everyone, but I think it still a cool way to do this so here is a new post about it.
Tunnels #
Recently I started using CloudFlare Tunnels more and more in my home projects (as well). Not going to argue about the fact weather you should use CF Tunnels - or not - I leave this decision to you. For me I am more or less OK with the concerns as:
- I put authentication in front of the tunnel
- I mostly expose non-mission-critical (ie. media, such as Plex, Jellyfin or ) services with it
Terraform #
Anyway, I am more into the technical discussion part of this. So when you set up a tunnel, the various web applications need to have their hostname defined on the tunnel (not to mention the tunnel itself); of course you could probably do this via the GUI (really? nah…in 2024 we know better!) or via cloudflared but since we all know and love/hate Terraform - why not use this tool?
OK, I might be walking into this Maslow’s instrument but come on, it is soo easy…
Lets see what we need:
- obviously you need a Cloudflare Account
- a server running web services and
cloudflared
(in my case this is a server running https://casaos.zimaspace.com/ currently) - and a machine running Terraform
direnv
Simple yeah?
Prep work #
Ok, lets get prepared
First I set up in the folder my direnv with a simple .envrc
file
You can get the values of those from Cloudflare. The one thing that tripped me over at first was the API token that you want to set up with these permissions to avoid disappointment:
Then we are good to go Terraform it…
In my case I created a few files (you know, separation and all!)
provider.tf
is the configuration for CloudFlaretunnels.tf
is for the tunnel’s configingress_rules.tf
are the public host names I want to use on my host- and
variables.tf
of course to create the variables matching to what I use in the.envrc
file
Now I am not going to recreate my tunnel, it is pretty straightforward - but the output should be something along the lines of:
tofu show
# cloudflare_record.dns_records["casa"]:
resource "cloudflare_record" "dns_records" {
allow_overwrite = false
content = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1.cfargotunnel.com"
created_on = "2024-10-26T12:31:00.209146Z"
hostname = "casa.domain.com"
id = "d04604ed446f4ac39fc05402fa226093"
metadata = {
"auto_added" = "false"
"managed_by_apps" = "false"
"managed_by_argo_tunnel" = "false"
}
modified_on = "2024-10-26T12:31:00.209146Z"
name = "casa"
proxiable = true
proxied = true
tags = []
ttl = 1
type = "CNAME"
zone_id = "d0c0fbdcd95438bc60860f16d45737db"
}
...number of other DNS records ..
# cloudflare_zero_trust_tunnel_cloudflared.existing_tunnel:
resource "cloudflare_zero_trust_tunnel_cloudflared" "existing_tunnel" {
account_id = "1443fe12026b33d56dcc26a9deed0667"
cname = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1.cfargotunnel.com"
id = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1"
name = "docker-tunnel-new"
secret = (sensitive value)
tunnel_token = (sensitive value)
}
# cloudflare_zero_trust_tunnel_cloudflared_config.tunnel_config:
resource "cloudflare_zero_trust_tunnel_cloudflared_config" "tunnel_config" {
account_id = "1443fe12026b33d56dcc26a9deed0667"
id = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1"
tunnel_id = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1"
config {
ingress_rule {
hostname = "casa.domain.com"
service = "http://localhost:8080"
}
... and so on ...
ingress_rule {
service = "http_status:404"
}
}
}
Outputs:
credentials_json = (sensitive value)
tunnel_id = "76e21b3d-e837-4a3c-ac0c-9959bbc422b1"
tunnel_name = "docker-tunnel-new"
tunnel_token = (sensitive value)
(note: yes, at some point I swapped from Terraform to OpenTofu but the process is the same with the good old TF as well…)
If you want to see the full code, you can grab it from my repo
Thanks for tunning in!